package org.jeecg.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.stream.Collectors;

@Aspect //定义一个切面 使用AOP打印出前端请求的接口信息
@Configuration
public class LogRecordAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);

    // 定义切点Pointcut
    @Pointcut("execution(* org.jeecg.modules.api.controller.*Controller.*(..))")
    public void excudeService() {
    }

    //@Around("execution(* org.jeecg.modules.api.controller.*Controller.*(..))")//直接引用
    @Around("excudeService()") //引用public void excudeService()方法
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        String method = request.getMethod();//请求方式为GET/POST
        String methodName=pjp.getSignature().getName();//请求controller层里面的方法名
        String controller=pjp.getTarget().getClass().getName();//请求的是哪个controller
        String uri = request.getRequestURI();//请求接口名
        String paraString = JSON.toJSONString(request.getParameterMap());//请求参数 只能接收@RequestParam注解的参数值
        //System.out.println("paraString="+paraString);
        String params=getParams(paraString);
        //System.out.println("params="+params);
        //System.out.println(JSON.toJSONString(pjp.getArgs()));//只接收参数的值,不接受参数名
        //如果@RequestParam的请求参数全部为空就进去
        if("{}".equals(paraString)){
            //如果用的是@RequestBody注解且用的是在如:Student对象上面
            String body=JSON.toJSONString(pjp.getArgs());//[{"age":0,"id":0,"name":"string","sex":"string"}]
            int bodyNum=pjp.getArgs().length;//参数值数量
            int nullNum=countStr(body,"null");//null值数量
            //如果是查询请求就不进去,没有使用@RequestParam注解和@RequestBody注解的方法
            if(!"[]".equals(body)){
                //如果请求参数值全部为null也不进去 body数量==null值数量
                if(bodyNum==nullNum){
                    params="";
                }else {
                    int firstNum=body.indexOf("[");
                    int lastNum=body.lastIndexOf("]");
                    body=body.substring(firstNum+1,lastNum);//[) 前闭后开
                    params=getBodyParams(body);
                }
            }
        }
        logger.info("SpringBoot action report -------- "+getDate()+" ------------------------------");
        logger.info("URI         : {}",uri);
        logger.info("Controller  : {}",controller+getTargetStack(controller));
        logger.info("method      : {}",method);
        logger.info("params      : {}",paraString);
        //System.out.println("SpringBoot action report -------- "+getDate()+" ------------------------------");
        //System.out.println("Uri         : "+uri);
        //System.out.println("Controller  : "+controller+getTargetStack(controller));
        //System.out.println("Method      : "+method);
        //System.out.println("MethodName  : "+methodName);
        //System.out.println("Params      : "+params);
        System.out.println("------------------------------------------------------------------------------------");
        // result的值就是被拦截方法的返回值
        Object result = pjp.proceed();//获得拦截方法的返回值
        logger.info("请求结束，controller的返回值是 " + JSON.toJSONString(result));
        //logger.info("********************请求结束*********************");
        logger.info("\n");

        //System.out.print("\n");//使控制台空一行,待下一次打印就不会挨在一起
        return result;//这里如果将拦截方法的返回值改为result=null,前端接收到的就是空的,就会变的没有返回值
    }

    //用StringBuffer定位调用的controller层 定位第一行
    private static String getTargetStack(String tag) {
        StringBuffer sb=new StringBuffer();
        int number=tag.lastIndexOf(".");//找到controller名称中最后一个.的索引
        String controller=tag.substring(number+1);//截取controller的名字
        sb.append(".("+controller).append(".java:1)");//定位在controller类里面的第一行
        return sb.toString();
    }

    //获取当前时间
    private String getDate(){
        Date date=new Date();
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(date);
    }

    //处理注解为@RequestParam 将请求参数变为name=张三这种格式
    private String getParams(String params){
        JSONObject object=JSONObject.parseObject(params);
        Set<String> keys =  object.keySet();
        String myValue="";
        String value="";
        for (String key : keys) {
            value=object.getString(key);//取出来的值为["张三"]这样 不能用replace替换,怕请求参数里面可能会有json数据比如[{},{}]这种类型的数据
            int firstNum=value.indexOf("\"");//第一个引号的索引
            int lastNum=value.lastIndexOf("\"");//最后一个引号的索引
            value=value.substring(firstNum+1,lastNum);
            myValue+=key+"="+value+"  ";
        }
        return myValue;
    }

    //处理@RequestBody注解的请求参数
    private String getBodyParams(String params){
        JSONObject object=JSONObject.parseObject(params);
        Set<String> keys =  object.keySet();
        String myValue="";
        String value="";
        for (String key : keys) {
            value=object.getString(key);
            myValue+=key+"="+value+"  ";
        }
        return myValue;
    }

    /**
     * @param str 原字符串
     * @param sToFind 需要查找的字符串
     * @return 返回在原字符串中sToFind出现的次数
     */
    private int countStr(String str,String sToFind) {
        int num = 0;
        while (str.contains(sToFind)) {
            str = str.substring(str.indexOf(sToFind) + sToFind.length());
            num ++;
        }
        return num;
    }
}

